/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System.Threading;
using System.Diagnostics.Contracts;

namespace Microsoft.Scripting
{
    public class ErrorSink
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security",
            "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public static readonly ErrorSink /*!*/
            Default = new ErrorSink();

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security",
            "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public static readonly ErrorSink /*!*/
            Null = new NullErrorSink();

        protected ErrorSink()
        {
        }

        /// <summary>
        /// This overload will be called when a SourceUnit is not available. This can happen if the code is being executed remotely,
        /// since SourceUnit cannot be marshaled across AppDomains.
        /// </summary>
        public virtual void Add(string message, string path, string code, string line, SourceSpan span, int errorCode,
                                Severity severity)
        {
            Contract.Requires(message != null);
            if (severity == Severity.FatalError || severity == Severity.Error)
            {
                throw new SyntaxErrorException(message, path, code, line, span, errorCode, severity);
            }
        }
    }

    internal sealed class NullErrorSink : ErrorSink
    {
        internal NullErrorSink()
        {
        }
    }

    public class ErrorCounter : ErrorSink
    {
        private readonly ErrorSink /*!*/ _sink;

        private int _fatalErrorCount;
        private int _errorCount;
        private int _warningCount;

        public int FatalErrorCount
        {
            get { return _fatalErrorCount; }
        }

        public int ErrorCount
        {
            get { return _errorCount; }
        }

        public int WarningCount
        {
            get { return _warningCount; }
        }

        public bool AnyError
        {
            get { return _errorCount > 0 || _fatalErrorCount > 0; }
        }

        public ErrorCounter()
            : this(ErrorSink.Null)
        {
        }

        public ErrorCounter(ErrorSink /*!*/ sink)
        {
            Contract.Requires(sink != null);
            _sink = sink;
        }

        protected virtual void CountError(Severity severity)
        {
            if (severity == Severity.FatalError) Interlocked.Increment(ref _fatalErrorCount);
            else if (severity == Severity.Error) Interlocked.Increment(ref _errorCount);
            else if (severity == Severity.Warning) Interlocked.Increment(ref _warningCount);
        }

        public void ClearCounters()
        {
            _warningCount = _errorCount = _fatalErrorCount = 0;
        }
    }
}